package com.cmas.platform.ws.handler;

import com.aidex.common.annotation.DataSource;
import com.aidex.common.enums.DataSourceType;
import com.aidex.common.utils.spring.SpringUtils;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.cmas.platform.config.NettyConfig;
import com.cmas.platform.domain.Message;
import com.cmas.platform.domain.Notification;
import com.cmas.platform.domain.UserRecord;
import com.cmas.platform.domain.em.MessageTypeEnum;
import com.cmas.platform.service.UserRecordService;
import com.cmas.platform.service.impl.UserRecordServiceImpl;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.Attribute;
import io.netty.util.AttributeKey;
import io.netty.util.concurrent.GlobalEventExecutor;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.Arrays;
import java.util.Collections;
import java.util.Objects;

/**
 * @Author su
 * @Date 2024/4/7 18:49
 */
@DataSource(value = DataSourceType.SLAVE)
@Component
@ChannelHandler.Sharable
public class WebSocketTextHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {


    /**
     * 存储在 Channel 中的 attr 属性名（存储用户Id）
     */
    public static final String USER_ID = "userId";


    public WebSocketTextHandler() {
        //启动WebSocketTextHandler
    }

    /**
     * 在新的 Channel 被添加到 ChannelPipeline 中时被调用。这通常发生在连接建立时，即 Channel 已经被成功绑定并注册到 EventLoop 中。
     * 在连接建立时被调用一次
     */
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        //将新连接的客户端Channel存储起来
        NettyConfig.getOnlineChannelGroup().add(channel);
    }

    /**
     * 在 WebSocket 连接断开时，Netty 会自动触发 channelInactive 事件，并将该事件交给事件处理器进行处理。
     * 在 channelInactive 事件的处理过程中，会调用 handlerRemoved 方法，用于进行一些资源释放等操作，确保 WebSocket 连接正常断开。
     */
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        //移除断开的客户端的Channel
        Channel channel = ctx.channel();
        cleanChannel(channel);
    }

    /**
     * 处理客户端非正常断开（WebSocket 连接发生异常时调用）
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        //获取断开连接的客户端的Channel
        Channel channel = ctx.channel();
        //移除断开的客户端的Channel
        cleanChannel(channel);
        //当发生异常时，手动关闭Channel
        channel.close();
    }

    /**
     * 当 Channel 的连接建立并准备好接收数据时被调用。这意味着连接已经成功建立，可以开始发送和接收数据了。
     * 在每次连接激活时被调用
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
    }

    /**
     * 当接收到前端发送的WebSocket时处理
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {

        //接收到的文本信息的二进制形式
        ByteBuf content = msg.content();

        //接收到的文本信息
        String text = msg.text();
        //获取到该条消息的标识，前端的字段必须后端的枚举名大小写一致
        //根据消息类型进行不同业务处理
        String type = JSON.parseObject(text).get("type").toString();
        MessageTypeEnum messageTypeEnum = MessageTypeEnum.valueOf(type);

        if (MessageTypeEnum.HEARTBEAT.compareTo(messageTypeEnum) == 0) {
            //接收到客户端的心跳
            Message heartbeatMessage = JSON.parseObject(text, Message.class);
        } else if (MessageTypeEnum.REGISTER.compareTo(messageTypeEnum) == 0) {
            //注册
            register(ctx, text);

        }

    }

    /**
     * 将连接的客户端注册到服务端中
     *
     * @param ctx
     * @param text
     */
    private void register(ChannelHandlerContext ctx, String text) {
        Message registerMessage = JSON.parseObject(text, Message.class);
        String userId = registerMessage.getFromId();
        //注册客户端
        //给 Channel 绑定一个存储 UserId 的 AttributeKey
        Channel channel = ctx.channel();
        //设置一个名为 userId 的 AttributeKey
        AttributeKey<Object> userIdKey = AttributeKey.valueOf("userId");
        //将 Channel 的 attr 设置一个名为 userId
        channel
                //在 Channel 中寻找名为 userIdKey 的 AttributeKey
                .attr(userIdKey)
                //给这个 AttributeKey 设置值
                .set(userId);
        //当自定义属性在属性集合中不存在时才进行添加
        //.setIfAbsent(userId);

        //将UserId与Channel建立联系
        NettyConfig.getOnlineUserChannelMap().put(userId, channel);

        //获取用户所在地区

        if (registerMessage.getContent() != null) {
            ChannelGroup channelGroup = NettyConfig.getOnlineUserGroupChannelMap().get(registerMessage.getContent());
            if (channelGroup == null) {
                channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
            }
            channelGroup.add(channel);
            NettyConfig.getOnlineUserGroupChannelMap().put(registerMessage.getContent(), channelGroup);
        }

        //通知所有用户都上线了
        // NettyConfig.getOnlineChannelGroup().writeAndFlush(new TextWebSocketFrame("用户：【" + userId + "】上线啦！"));
    }

    /**
     * 给指定的用户发送消息
     *
     * @param textMessageJson
     */
    private void sendUserMsg(String textMessageJson) {
        //获取接收到的消息的实体类
        Message message = JSON.parseObject(textMessageJson, Message.class);
        String userId = message.getFromId();
        String userMsg = message.getContent();
        String receiver = message.getToId();
        //给指定的用户发送消息
        Channel receiverChannel = NettyConfig.getOnlineUserChannelMap().get(receiver);
        if (Objects.nonNull(receiverChannel)) {
            receiverChannel.writeAndFlush(new TextWebSocketFrame(userId + "：" + userMsg));
        }
    }




    /**
     * 删除断开连接的客户端在程序中的数据
     *
     * @param channel 断开连接的客户端的 Channel
     */
    private void cleanChannel(Channel channel) {
        //获取客户端 Channel 中存储的名为 fromId 的 Attribute
        Attribute<String> userIdKey = channel.attr(AttributeKey.valueOf(USER_ID));
        String userId = userIdKey.get();
        //从 ChannelGroup 中移除断开的 Channel
        NettyConfig.getOnlineChannelGroup().remove(channel);
        //从 Map 中移除 UserId 与 Channel 的对照关系
        NettyConfig.getOnlineUserChannelMap().remove(userId);

        //通知所有用户某用户下线了
        //NettyConfig.getOnlineChannelGroup().writeAndFlush(new TextWebSocketFrame("用户：【" + userId + "】下线啦！"));
    }

    /**
     * 检查给定的字符串是否不是空串、空格、null
     *
     * @param strs 需要检查的字符串
     */
    private boolean checkHasText(String... strs) {
        return Arrays.stream(strs).sequential().allMatch(StringUtils::hasText);
    }

}
